#include <stdint.h>
#include <stdfunc.h>
#include <memory.h>
#include <multiboot.h>
#include <text_video.h>
#include <task.h>

#define MAX_FREE    256

uint32_t base;
volatile uint32_t num_freed_blocks;

static struct memdesc
{
    uint32_t base;
    uint32_t size;
} memdesc[MAX_FREE];

struct usable_memory
{
    uint32_t base;
    uint32_t end;
} usable_memory[256];


void init_memory(void)
{
    uint16_t i;
    
    for(i = 0; i < 256; i++)
    {usable_memory[i].base = usable_memory[i].end = 0;}
    
    num_freed_blocks = 0;

    for(i = 0; i < MAX_FREE; i++)
    {memdesc[i].base = memdesc[i].size = 0;}

    base = (uint32_t)&kernel_end;
    
    printf("kernel loaded at %x-%x (%uKB) \n\n", (uint32_t)&kernel_start, (uint32_t)&kernel_end, ((uint32_t)&kernel_end - (uint32_t)&kernel_start)/1024);
}

uint8_t * memalloc(const uint32_t count, uint8_t alignment)
{
    uint32_t i, found_free_blocks;
    uint32_t block;
    
    if(alignment % 2){alignment++;}
    if(alignment > 32){alignment = 32;}
    
    if(!count)
    {
        printf("SYS: attempted memalloc of size 0\n");
        return (uint8_t*)(-1);
    }
    
    if(count >= 0x4000000)
    {
        printf("SYS: attempted memalloc of a size >= 64M\n");
        return (uint8_t*)(-1);
    }
    
    found_free_blocks = 0;
    
    if(alignment > 1)
    {
        while(find_usable_memory(base, count) == false){base++;}
    
        while(base & (alignment-1))
        {base++;}
        block = base;
        base += count;
        return (uint8_t*)block;
    }
    
    if(num_freed_blocks)
    {
        for(i = 0; i < MAX_FREE; i++)
        {
            if(memdesc[i].base)
            {
                if(memdesc[i].size == count)
                {
                    block = memdesc[i].base;
                    memdesc[i].base = 0;
                    memdesc[i].size = 0;
                    num_freed_blocks--;
                    return (uint8_t*)block;
                }
                
                if(memdesc[i].size > count)   
                {
                    block = memdesc[i].base;
                    memdesc[i].base += count;
                    memdesc[i].size -= count;
                    return (uint8_t*)block;
                }
            }
        }
    }
    
    while(find_usable_memory(base, count) == false){base++;}
    block = base;
    base += count;
    
    return (uint8_t*)block;
}

bool memfree(const uint8_t * const mem, const uint32_t count)
{
    uint32_t i;

    if(!count)
    {return false;}
    
    if(!mem)
    {
        printf("SYS: cannot memfree memory location 0\n");
        return false;
    }
    
    if((uint32_t)mem < 0x100000)
    {
        printf("SYS: cannot memfree memory below 1MB\n");
        return false;
    }
    
    if(((uint32_t)mem + count) >= (uint32_t)&kernel_start && (uint32_t)mem < (uint32_t)&kernel_end)
    {
        printf("SYS: cannot memfree kernel memory space\n");
        return false;
    }
    
    if(num_freed_blocks >= MAX_FREE-1)
    {
        printf("SYS: no more free slots for use by memfree, ignoring...\n");
        return false;
    }
    
    for(i = 0; i < MAX_FREE; i++)
    {
        if(!memdesc[i].base && !memdesc[i].size)
        {
            memdesc[i].base = (uint32_t)mem;
            memdesc[i].size = count;
            num_freed_blocks++;
            return true;
        }
    }

    return false;
}

bool set_usable_memory(const uint32_t mem, const uint32_t count)
{
    uint16_t i;

    if(!count){return false;}

    for(i = 0; i < 256; i++)
    {
        if(!usable_memory[i].base && !usable_memory[i].end)
        {
            usable_memory[i].base = mem;
            usable_memory[i].end = mem+count;
            return true;
        }
    }
    
    return false;
}

bool find_usable_memory(const uint32_t mem, const uint32_t count)
{
    uint16_t i;
    
    //if(mem < USABLE_RAM_LOW){return false;}
    //if((mem + count) >= USABLE_RAM_HIGH){return false;}
    
    for(i = 0; i < 256; i++)
    {
        if(mem >= usable_memory[i].base &&
           (mem + count) < usable_memory[i].end)
        {
            return true;
        }
    }
    
    return false;
}

void set_memory_16(const uint8_t * const mem, const uint16_t val, const uint32_t count)
{
    uint32_t i;
    
    for(i = 0; i < count; i += 2)
    {*(uint16_t*)(mem + i) = val;}
}

/* clear "count" bytes of memory starting at "mem" */
void clear_memory(const uint8_t * const mem, const uint32_t count)
{
    uint32_t i;
    
    for(i = 0; i < count; i++)
    {*(uint8_t*)(mem + i) = 0x00;}
}

/* copy "count" bytes of memory from "src" to "dst" */
void copy_memory(const uint8_t * const dst, const uint8_t * const src, const uint32_t count)
{
    uint32_t i;
    
    for(i = 0; i < count; i++)
    {*(uint8_t*)(dst + i) = *(uint8_t*)(src + i);}
}

uint8_t compare_memory(const uint8_t * const mem1, const uint8_t * const mem2, const uint32_t count)
{
    uint32_t i;
    
    for(i = 0; i < count; i++)
    {
        if(mem1[i] > mem2[i]){return 1;}
        else if (mem1[i] < mem2[i]){return (uint8_t)(-1);}
    }
    
    return 0;
}

uint32_t strlen(const char * const str)
{
    uint32_t i;

    i = 0;
    while(str[i]){i++;}
    
    return i;
}

